# Provision.pm
# Copyright (C) 2006, 2007  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information visit http://carrierclass.net/
#

package CCN::Provision;
use strict; use warnings;
use base qw(CCN::Provision);

# Provision.pm
# Copyright (C) 2006, 2007  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information visit http://carrierclass.net/
#

package CCNCore::Provision;
use strict; use warnings;
use base qw(CCNCore::Provision::Base);

# These must be re-defined by subclasses.

# Return the class'es version of ::spool
sub spool_module
{
    return qw(
        CCNCore::Provision::Application::spool
    );
}

sub bind_module
{
    return qw(
        CCNCore::Provision::Application::bind
    );
}

sub dhcpd_module
{
    return qw(
        CCNCore::Provision::Application::dhcpd
    );
}

# Any module that can be ran before we populate the databases.
sub pre_db_modules
{
    return qw(
        CCNCore::Provision::Database::mysql
    );
}

# Modules that populate the database; must always start with the subclass'es
# version of startup_mysql.
sub db_modules
{
    return qw(
        CCNCore::Provision::startup_mysql
    );
}

# Other modules that must be ran after the databases are up and running.
sub post_db_modules
{
    return ();
}

# Subclass'es version of commit.pl, if needed.
sub commit_template
{
    return CCNCore::Home::manager.'/lib/CCNCore/templates/commit.pl';
}



sub run
{
    my $self = shift;
    my $p = $self->p;

    unlink CCNCore::Home::config.'/local/config.ok' unless $p->force_manager;

    use CCNCore::Scripting;
    unless($p->force_manager)
    {
        CCNCore::Scripting::start($self->commit_template);
    }

    $self->run_modules();

    unless($p->force_manager)
    {
        CCNCore::Scripting::end();
    }

    if($self->success and not $p->force_manager)
    {
        open(my $fh,'>',CCNCore::Home::config.'/local/config.ok');
        print $fh time();
        close($fh);
    }

    $self->finish();
}

our $populate_script    = CCNCore::Home::config.'/local/bin/commit-populate-db.pl';
our $post_db_script     = CCNCore::Home::config.'/local/bin/commit-post-db.pl';

sub start_db
{
    my $self = shift;

    # End the pre-db script
    CCNCore::Scripting::commit_end();

    if( defined $self->p->{db_member} and $self->p->system_pk == $self->p->primary_db_server )
    {
        # Once the database initialization has been done, populate them.
        # (This script is ran once complete_grant_completed is reached.)
        CCNCore::Scripting::commit_start_file($self->commit_template,$populate_script,'populate');
        CCN::Scripting::exec_command("chmod +x '$populate_script'");
    }
    # If we are not the primary server, the Provision::Database::* module dependencies will
    # be skipped, and we'll start over at commit-post-db below.
}

sub start_post_db
{
    my $self = shift;
 
    # Primary server
    if( defined $self->p->{db_member} and $self->p->system_pk == $self->p->primary_db_server )
    {
        # End the populate-db script
        CCNCore::Scripting::commit_end();
    }

    CCNCore::Scripting::commit_start_file($self->commit_template,$post_db_script,'postdb');
    CCN::Scripting::exec_command("chmod +x '$post_db_script'");
}


# Note: the series up to startup_mysql are the core series.
#       They are committed by commit.pl
# The series after startup_mysql all depend on the database to
# be ready; they wait until so.

sub _phase1_modules
{
    my $self = shift;
    return (
        qw(
            CCNCore::Provision::Start
            CCNCore::IPC::provision
        ),
        $self->spool_module(),
        qw(
            CCNCore::Provision::Application::resolv
            CCNCore::Provision::Application::interfaces
            CCNCore::Provision::Application::syslog
        ),
        $self->bind_module(),
        qw(
            CCNCore::Provision::Application::sshd
            CCNCore::Provision::Application::ejabberd
            CCNCore::Provision::Application::ntpd
            CCNCore::Provision::Application::jabber_client

            CCNCore::Provision::distribute_keys

            CCNCore::Provision::Application::Billing
            CCNCore::Provision::Application::Backup
        ),
        $self->dhcpd_module(),
        $self->pre_db_modules(),
    );
}

sub _phase2_modules
{
    my $self = shift;

    # Must include the local version of Provision::startup_mysql
    return (
        $self->db_modules(),
        qw(
            CCNCore::Provision::Database::CCNCore
        ),
    );
}

sub _phase3_modules
{
    my $self = shift;
    return (
        $self->post_db_modules(),
        qw(
            CCNCore::Provision::Application::apache
            CCNCore::Provision::End
        ),
    );
}

sub _all_modules
{
    my $self = shift;
    return (
        $self->_phase1_modules,
        $self->_phase2_modules,
        $self->_phase3_modules,
    );
}

sub run_modules()
{
    my $self = shift;

    $self->_run_module_list($self->_phase1_modules);
    
    $self->start_db();
    
    $self->_run_module_list($self->_phase2_modules);

    $self->start_post_db();
    
    $self->_run_module_list($self->_phase3_modules);
}




sub _run_module_list
{
    our $self = shift;
    $self->_for_module_list(
        sub
        {
            my $c = shift;
            $c->run();
            my $ok = $c->finish();
            # The module reported errors.
            $self->error("Module $s reported errors") if not $ok;
        }
    );
}

sub register_modules
{
    our $self = shift;
    $self->_for_module_list( sub { shift->register() } );
}

sub _for_module_list
{
    my $self = shift;
    my $what = shift;
    my $p = $self->p;
    our @modules = $_;
    for my $s (@_)
    {
        $self->log("Starting module $s\n");        
        my $c;
        eval "use $s; \$c = new $s(\$p);";
        # Eval failed
        $self->die("Module $s: $@") if $@;
        $self->die("Module $s: creation failed") unless defined $c;
        $what->($c);
        undef $c;
    }
}

=pod
sub rewrite
{
    my $self = shift;
    my $base = ref($self);
    $base =~ s/^([^:]+)::.*$/$1/;
    
    my @reply = ();
    
    use CCNCore::Home;
    for my $name (shift @_)
    {
        my $file = CCNCore::Home.'/lib';
        my $newname = $name;
        $newname =~ s/^([^:]+)::/${base}::/;
        
        my $filename = $newname;
        $filename =~ s/::/\//;
        
        if( -e $file.$filename)
        {
            push @reply, $newname;
        }
        else
        {
            push @reply, $name;
        }
    }
    return @reply;
}
=cut


1;